package com.vkdroid.xmpp;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import com.vkdroid.Constants;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.StringUtils;

/**
 * Created by Vladimir Grachev.
 * Date: Aug 28, 2010
 * Time: 11:00:51 PM
 */
public class XmppService extends Service {

    private static final String TAG = "[vkdroid:XmppService]";

    private XmppServiceBinder mBinder;
    private XMPPConnection mConnection;

    private RemoteCallbackList<IXmppServiceCallback> mCallbacks;


    @Override
    public void onCreate() {
        super.onCreate();

        mBinder = new XmppServiceBinder();
        mCallbacks = new RemoteCallbackList<IXmppServiceCallback>();
        // Create the configuration for this new mConnection
        ConnectionConfiguration config = new ConnectionConfiguration(Constants.XMPP_SERVER_URL, Constants.XMPP_SERVER_PORT);
        config.setCompressionEnabled(true);
        config.setSASLAuthenticationEnabled(false);

        mConnection = new XMPPConnection(config);
    }

    @Override
    public void onDestroy() {

        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return true;
    }
    
    public class XmppServiceBinder extends IXmppService.Stub {

        public void connect(final String username, final String password) throws RemoteException {
            Log.d(TAG, "    Connect: " + username + "/" + password);

            new Thread(new Runnable() {
                public void run() {
                    try {
                        mConnection.connect();
                        mConnection.login(username, password);

                        PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
                        mConnection.addPacketListener(new PacketListener() {
                            public void processPacket(Packet packet) {

                                Log.i(TAG, "processPacket: chat");

                                Message message = (Message) packet;

                                Log.d(TAG, "Message: " + message.toXML());

                                if (message.getBody() != null) {
                                    String fromName = StringUtils.parseBareAddress(message.getFrom());
                                    Log.i(TAG, "Got text [" + message.getBody() + "] from [" + fromName + "]");
                                    notifyServiceListenersOnMessageReceived(fromName, message.getBody());
                                }
                            }
                        }, filter);

                        mConnection.addPacketListener(new PacketListener() {
                            public void processPacket(Packet packet) {

                                Log.i(TAG, "processPacket: normal");

                                Message message = (Message) packet;
                                if (message.getBody() != null) {
                                    String fromName = StringUtils.parseBareAddress(message.getFrom());
                                    Log.i(TAG, "Got text [" + message.getBody() + "] from [" + fromName + "]");
                                }
                            }
                        }, new MessageTypeFilter(Message.Type.normal));

                        mConnection.addPacketListener(new PacketListener() {
                            public void processPacket(Packet packet) {

                                Log.i(TAG, "processPacket: groupchat");

                                Message message = (Message) packet;
                                if (message.getBody() != null) {
                                    String fromName = StringUtils.parseBareAddress(message.getFrom());
                                    Log.i(TAG, "Got text [" + message.getBody() + "] from [" + fromName + "]");
                                }
                            }
                        }, new MessageTypeFilter(Message.Type.groupchat));
                        
                        mConnection.addPacketListener(new PacketListener() {
                            public void processPacket(Packet packet) {

                                Log.i(TAG, "processPacket: headline");

                                Message message = (Message) packet;
                                if (message.getBody() != null) {
                                    String fromName = StringUtils.parseBareAddress(message.getFrom());
                                    Log.i(TAG, "Got text [" + message.getBody() + "] from [" + fromName + "]");
                                }
                            }
                        }, new MessageTypeFilter(Message.Type.headline));

                        mConnection.addPacketListener(new PacketListener() {
                            public void processPacket(Packet packet) {

                                Log.i(TAG, "processPacket: error");

                                Message message = (Message) packet;
                                if (message.getBody() != null) {
                                    String fromName = StringUtils.parseBareAddress(message.getFrom());
                                    Log.i(TAG, "Got text [" + message.getBody() + "] from [" + fromName + "]");
                                }
                            }
                        }, new MessageTypeFilter(Message.Type.error));

                        if (mConnection.isAuthenticated()) {
                            notifyServiceListenersOnConnect();
                        }

                    } catch (XMPPException e) {
                        e.printStackTrace();
                    }

                    Log.d(TAG, "    isConnected: " + mConnection.isConnected());
                    Log.d(TAG, "    isAuthenticated: " + mConnection.isAuthenticated());
                }
            }).start();
        }

        public void disconnect() throws RemoteException {
            Log.d(TAG, "    isConnected: " + mConnection.isConnected());
            Log.d(TAG, "    isAuthenticated: " + mConnection.isAuthenticated());


            Log.d(TAG, "    Disconnect.");

            new Thread(new Runnable() {
                public void run() {
                    mConnection.disconnect();
                    notifyServiceListenersOnDisconnect();
                }
            }).start();
        }

        public void sendMessage(String recipient, String message) throws RemoteException {

            Log.i("XMPPClient", "Sending text [" + message + "] to [" + recipient + "]");
            Message msg = new Message(recipient, Message.Type.chat);
            msg.setBody(message);
            mConnection.sendPacket(msg);

        }

        public void registerCallback(IXmppServiceCallback cb) throws RemoteException {
            if (cb != null){
                mCallbacks.register(cb);
            }
        }

        public void unregisterCallback(IXmppServiceCallback cb) throws RemoteException {
            if (cb != null){
                mCallbacks.unregister(cb);
            }
        }
    }

    private synchronized void notifyServiceListenersOnConnect() {
        if (mCallbacks != null) {
            final int N = mCallbacks.beginBroadcast();
            for (int i = 0; i < N; i++) {
                try {
                    mCallbacks.getBroadcastItem(i).onConnect();
                } catch (RemoteException e) {

                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private synchronized void notifyServiceListenersOnDisconnect() {
        if (mCallbacks != null) {
            final int N = mCallbacks.beginBroadcast();
            for (int i = 0; i < N; i++) {
                try {
                    mCallbacks.getBroadcastItem(i).onDisconnect();
                } catch (RemoteException e) {

                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private synchronized void notifyServiceListenersOnMessageReceived(String from, String message) {
        if (mCallbacks != null) {
            final int N = mCallbacks.beginBroadcast();
            for (int i = 0; i < N; i++) {
                try {
                    mCallbacks.getBroadcastItem(i).onMessageReceived(from, message);
                } catch (RemoteException e) {

                }
            }
            mCallbacks.finishBroadcast();
        }
    }

}
